package org.geektimes.configuration.microprofile.config.sources;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;
import java.util.stream.Stream;

import org.eclipse.microprofile.config.spi.ConfigSource;
import org.eclipse.microprofile.config.spi.ConfigSourceProvider;

public class ConfigSources implements ConfigSourceProvider, Iterable<ConfigSource> {

	private List<ConfigSource> configSources = new ArrayList<ConfigSource>();

	private boolean addedDefaultSources = false;

	private boolean addedDiscoveredSources = false;

	private ClassLoader classLoader;

	public ConfigSources(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}

	@SuppressWarnings("unchecked")
	public void addDefaultSources() {

		if (addedDefaultSources)
			return;
		addSources(JavaSystemPropertiesConfigSource.class, OperationEnvironmentConfigSource.class,
				DefaultConfigSource.class);

		addedDefaultSources = true;
	}

	public void addDiscoveredSources() {

		if (addedDiscoveredSources)
			return;
		addSources(ServiceLoader.load(ConfigSource.class, classLoader));

	}

	@SuppressWarnings("unchecked")
	public void addSources(Class<? extends ConfigSource>... sources) {
		addSources(Stream.of(sources).map(this::newInstance).toArray(ConfigSource[]::new));
	}

	public void addSources( ConfigSource... sources) {
		addSources(Arrays.asList(sources));
	}

	public void addSources(Iterable<ConfigSource> configSources) {
		
		configSources.forEach(this.configSources::add);
		
		Collections.sort(this.configSources, ConfigSourceComparator.INSTANCE);
	}

	private ConfigSource newInstance(Class<? extends ConfigSource> source) {
		ConfigSource instance = null;

		try {
			instance = source.newInstance();
		} catch (InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
		}

		return instance;
	}

	@Override
	public Iterable<ConfigSource> getConfigSources(ClassLoader forClassLoader) {
		return this.configSources;
	}

	@Override
	public Iterator<ConfigSource> iterator() {
		return this.configSources.iterator();
	}

	public void setClassLoader(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}
	
	

}
